package org.budo.maven.plugin.assembly.mojo.processor;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.budo.maven.plugin.assembly.mojo.util.MojoGitLogUtil;
import org.budo.support.lang.util.FileUtil;
import org.budo.support.lang.util.IoUtil;
import org.budo.support.yaml.util.YamlUtil;
import org.budo.time.Time;

/**
 * @author lmw
 */
public class BudoMavenAssemblySpringBootProcessor extends AbstractBudoMavenAssemblyProcessor {
    public BudoMavenAssemblySpringBootProcessor(MavenProject project, MavenSession session, String format, Log log) {
        super(project, session, format, log);
    }

    @Override
    public void execute() throws Throwable {
        this.getLog().info("#32 BudoMavenAssemblySpringBootProcessor#execute project.artifact=" + this.getProject().getArtifact() //
                + ", project.base=" + this.getProject().getBasedir() + ", format=" + this.getFormat() + ", this=" + this);

        // 清理目标文件夹
        String destPath = this.getProject().getBuild().getDirectory() + File.separator + this.getProject().getArtifactId() + "-assembly";
        FileUtil.deleteIfExists(destPath);

        // 将 SpringBoot 的 application.yml 转为 application.properties，在copyConf后执行
        // this.convertYamlToProperties();

        // 写入git版本，到conf
        MojoGitLogUtil.writeGitLogToConf(this.getProject(), this.getLog());

        // 复制 FatJar
        this.copyFatJar();

        // 复制bin
        this.writeBin();

        // 复制 Docker
        this.writeDocker();

        // 将自身Jar包 去掉resources后 放入lib
        // this._copySelfJar(); // spring boot 项目

        this.writeReadme();

        // 压缩
        this.compress();
    }

    protected void _copySelfJar() throws Throwable {
        String dirTarget = this.getProject().getBuild().getDirectory();
        String jarFileName = this.getProject().getArtifactId() + "-" + this.getProject().getVersion() + ".jar";

        File jarTo = new File(dirTarget //
                + File.separator + this.getProject().getArtifactId() + "-assembly" //
                + File.separator + "lib" //
                + File.separator + jarFileName);

        String jarOriginal = this.getProject().getArtifactId() + "-" + this.getProject().getVersion() + ".jar.original";
        File jarOriginalFile = new File(dirTarget + File.separator + jarOriginal);

        if (jarOriginalFile.exists()) { // spring-boot 项目
            this.getLog().info("#483 copySelfJar, jarFrom=" + jarOriginalFile + ", jarTo=" + jarTo);
            this.copyJarSkipResources(jarOriginalFile, jarTo);
            return;
        }

        // 普通项目
        File jarFrom = new File(dirTarget + File.separator + jarFileName); // 非 spring-boot 非 war 项目

        this.getLog().info("#491 copySelfJar, jarFrom=" + jarFrom + ", jarTo=" + jarTo);
        this.copyJarSkipResources(jarFrom, jarTo);
        return;
    }

    /**
     * 复制 jar 排除 resource
     * 
     * @param from jar文件原位置
     * @param to   lib目录中的这个jar的目标位置
     */
    private void copyJarSkipResources(File from, File to) throws Throwable {
        if (!from.exists()) {
            this.getLog().warn("#504 copyJarSkipResources, selfJar not exists, " + from);
            return;
        }

        ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(to));

        // 遍历jar
        ZipFile zipFile = new ZipFile(from);
        Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();

        while (zipEntries.hasMoreElements()) {
            ZipEntry zipEntry = zipEntries.nextElement();

            String resourcePath = this.findInResource(zipEntry.getName());
            if (null != resourcePath) { // 新的 jar 中跳过 resources 里面的文件
                this.getLog().debug("#276 skip resource file " + zipEntry + ", " + resourcePath);
                continue;
            }

            this.getLog().debug("#280 add selfJar entry " + zipEntry);

            zipOut.putNextEntry(zipEntry);
            InputStream inputStream = zipFile.getInputStream(zipEntry);
            IoUtil.write(inputStream, zipOut, false, true);
            zipOut.closeEntry();
        }

        zipOut.close();
        zipFile.close();
    }

    /**
     * 暂未使用 将 SpringBoot 的 application.yml 转为 application.properties
     */
    protected void convertYamlToProperties() {
        try {
            this.doConvertYamlToProperties();
        } catch (Throwable e) {
            this.getLog().error("#285 convertYamlToProperties error", e);
        }
    }

    private void doConvertYamlToProperties() {
        File confDir = new File(this.getProject().getBuild().getDirectory() + File.separator //
                + this.getProject().getArtifactId() + "-assembly" + File.separator + "conf");

        File yml = new File(confDir, "application.yml");
        File props = new File(confDir, "application.properties");
        if (!yml.exists() || props.exists()) {
            this.getLog().info("#195 no application.yml or application.properties, confDir=" + FileUtil.getCanonicalPath(confDir));
            return;
        }

        InputStream ymlInputStream = IoUtil.fileInputStream(yml);
        Map<String, String> ymlMap = YamlUtil.ymlToMap(ymlInputStream);

        this.getLog().info("#195 application.yml found, " + FileUtil.getCanonicalPath(yml));

        String ymlProperties = "#\n";
        ymlProperties += "#Created By budo-maven-assembly-plugin from application.yml\n";
        ymlProperties += "#" + Time.now().toString("yyyy-MM-dd HH:mm:ss.SSS") + "\n";
        ymlProperties += "#\n\n";

        for (Entry<String, String> entry : ymlMap.entrySet()) {
            ymlProperties += entry.getKey() + "=" + entry.getValue() + "\n\n";
        }

        IoUtil.write(ymlProperties.getBytes(), props);
    }

    private void copyFatJar() {
        String target = this.getProject().getBuild().getDirectory();
        String finalName = this.getProject().getBuild().getFinalName();
        String artifactId = this.getProject().getArtifactId();

        File from = new File(target + File.separator + finalName + ".jar");

        File assemblyDir = new File(target + File.separator + artifactId + "-assembly");
        File to = new File(FileUtil.getCanonicalPath(assemblyDir) + File.separator + artifactId + ".jar");

        FileUtil.copyFile(from, to);

        this.getLog().info("#93 after copyFatJar, from " + FileUtil.getCanonicalPath(from) + " to " + FileUtil.getCanonicalPath(to));
    }
}
